/**
 * 
 */
package name.marz.to.go.btree.ui.swing;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.util.Map;

import name.marz.to.go.btree.node.BTree;
import name.marz.to.go.btree.node.Node;
import name.marz.to.go.btree.node.NodeVisitor;


/**
 * @author masaru-gotou
 *
 */
public class TreeDrawer implements NodeVisitor {
	private NodeDrawer drawer;
	private Map<Node, NodeExtent> extents;
	private GraphicsConfigration config;
	private Graphics g;
	private ExtentCalculator calc;
	private Point curPoint;
	private Point parPoint;
	private Dimension bounds;
	
	public TreeDrawer(GraphicsConfigration gc) {
		config = gc;
		calc = new ExtentCalculator(gc);
	}
	
	public <T> void draw(BTree<T> tree,Graphics g) {
		draw(tree,g,config.getRootPoint());
	}
	public <T> void draw(BTree<T> tree,Graphics g, Point rootPoint) {
		this.g = g;
		drawer = new NodeDrawer(g,config);
		curPoint = rootPoint;
		parPoint = null;
		tree.getRoot().accept(calc);
		extents = calc.getExtents();
		bounds = extents.get(tree.getRoot()).getSize();
		tree.getRoot().accept(this);
	}
	public Dimension getBounds() {
		return bounds;
	}
	
	/* (non-Javadoc)
	 * @see btree.node.NodeVisitor#visit(btree.node.Node)
	 */
	@Override
	public void visit(Node node) {
		// 親子を結ぶ線を描画
		if ( parPoint != null ) {
			g.drawLine(curPoint.x,curPoint.y,parPoint.x,parPoint.y);
		}
		
		NodeExtent extent = extents.get(node);
		// 左側ポインタの先を描画
		Object leftpointer = node.getLeftPointer();
		int trans_x = -extent.width / 2;
		if ( leftpointer instanceof Node ) {
			Node leftnode = (Node)leftpointer;
			NodeExtent leftExtent = extents.get(leftnode);
			Point saveCurPoint = curPoint;
			Point saveParPoint = parPoint;
			parPoint = curPoint;
			curPoint = new Point(curPoint.x+trans_x+leftExtent.width/2,
								 curPoint.y+config.getNodeHeight()+config.getNodeVerticalMirgin());
			leftnode.accept(this);
			curPoint = saveCurPoint;
			parPoint = saveParPoint;
			trans_x += leftExtent.width;
		}
		// その他のポインタの先を描画
		for ( int i=0; i<node.getMaxKeys(); i++ ) {
			Object pointer = node.getPointer(i);
			if ( pointer instanceof Node ) {
				NodeExtent e = extents.get(pointer);
				Point saveCurPoint = curPoint;
				Point saveParPoint = parPoint;
				parPoint = curPoint;
				curPoint = new Point(curPoint.x+trans_x+e.width/2,
									 curPoint.y+config.getNodeHeight()+config.getNodeVerticalMirgin());
				((Node)pointer).accept(this);
				curPoint = saveCurPoint;
				parPoint = saveParPoint;
				trans_x += e.width;
			}
		}
		// 自分を描画
		drawer.draw(curPoint, node);
	}
}
